#ifndef SIMPLE_GEOM_BOOL_ALGEBRA_HPP
#define SIMPLE_GEOM_BOOL_ALGEBRA_HPP

#include "reduction.hpp"
#include "vector.hpp"

namespace simple::geom
{

	struct conjunct
	{
		static constexpr bool identity = true;
		// NOTE: passing by value here prevented short circuiting
		// gcc 9.3.0
		// gcc 7.5.0
		constexpr bool operator()(const bool& a, const bool& b) const noexcept
		{
			return a && b;
		}
	};

	struct disjunct
	{
		static constexpr bool identity = false;
		// NOTE: passing by value here prevented short circuiting
		// gcc 9.3.0
		// gcc 7.5.0
		constexpr bool operator()(const bool& a, const bool& b) const noexcept
		{
			return a || b;
		}
	};

	// TODO: guess need to support compatibility_tag from support/array_operators.hpp :/
#define SIMPLE_GEOM_VECTOR_DEFINE_COMPARISON_OPERATOR(op, reduce_method) \
	template <typename C1, typename C2, size_t D, typename O, \
		typename Bool = \
			typename vector<C1, D, O>::template map_coordinate_t<bool> \
	> \
	[[nodiscard]] \
	constexpr reduction<reduce_method, Bool, bool> \
	operator op(const vector<C1,D,O>& one, const vector<C2,D,O>& other) \
	{ \
		Bool ret{}; \
		for(size_t i = 0; i < D; ++i) \
			ret[i] = (one[i] op other[i]); \
		return reduction<reduce_method, Bool, bool>(ret); \
	}

SIMPLE_GEOM_VECTOR_DEFINE_COMPARISON_OPERATOR(==, conjunct)
SIMPLE_GEOM_VECTOR_DEFINE_COMPARISON_OPERATOR(!=, disjunct)
SIMPLE_GEOM_VECTOR_DEFINE_COMPARISON_OPERATOR(>,  conjunct)
SIMPLE_GEOM_VECTOR_DEFINE_COMPARISON_OPERATOR(>=, conjunct)
SIMPLE_GEOM_VECTOR_DEFINE_COMPARISON_OPERATOR(<,  conjunct)
SIMPLE_GEOM_VECTOR_DEFINE_COMPARISON_OPERATOR(<=, conjunct)
#undef SIMPLE_GEOM_VECTOR_DEFINE_COMPARISON_OPERATOR

	template <typename C, size_t D, typename O>
	using conjunction = reduction<
		conjunct, vector<C,D,O> >;

	template <typename C, size_t D, typename O>
	using disjunction = reduction<
		disjunct, vector<C,D,O> >;

	template <typename C, size_t D, typename O>
	[[nodiscard]] constexpr
	conjunction<C,D,O> to_conjunction(vector<C,D,O> v)
	{
		return conjunction<C,D,O>(v);
	}

	template <typename C, size_t D, typename O>
	[[nodiscard]] constexpr
	conjunction<C,D,O> to_conjunction(disjunction<C,D,O> v)
	{
		return conjunction<C,D,O>(v.range);
	}

	template <typename C, size_t D, typename O>
	[[nodiscard]] constexpr
	disjunction<C,D,O> to_disjunction(vector<C,D,O> v)
	{
		return disjunction<C,D,O>(v);
	}

	template <typename C, size_t D, typename O>
	[[nodiscard]] constexpr
	disjunction<C,D,O> to_disjunction(conjunction<C,D,O> v)
	{
		return disjunction<C,D,O>(v.range);
	}

	// De Morgan's laws
	template <typename C, size_t D, typename O>
	[[nodiscard]] constexpr conjunction<C,D,O>
	operator~(const disjunction<C,D,O>& one) noexcept
	{
		return conjunction<C,D,O>(~(one.range));
	}
	template <typename C, size_t D, typename O>
	[[nodiscard]] constexpr disjunction<C,D,O>
	operator~(const conjunction<C,D,O>& one) noexcept
	{
		return disjunction<C,D,O>(~(one.range));
	}

} // namespace simple::geom

namespace simple
{
	template <typename C, size_t D, typename O>
	struct support::array_operator_implicit_conversion<
		geom::disjunction<C,D,O>>
	{
		using type = typename geom::disjunction<C,D,O>::
			range_type::template map_coordinate_t<bool>;
	};

	template <typename C, size_t D, typename O>
	struct support::array_operator_implicit_conversion<
		geom::conjunction<C,D,O>>
	{
		using type = typename geom::conjunction<C,D,O>::
			range_type::template map_coordinate_t<bool>;
	};
} // namespace simple

#endif /* end of include guard */
